"""DateUtils类 - 提供日期和时间处理相关的实用功能"""

import time
import calendar
from datetime import datetime, timedelta, date
from typing import Union, Optional, Tuple, List

from rpa_framework.rpa_logging.logger import Logger
from rpa_framework.exceptions.rpa_exceptions import DateProcessingException

class DateUtils:
    """日期工具类，提供日期解析、格式化、计算等功能"""
    
    def __init__(self):
        """初始化日期工具类"""
        self.logger = Logger("DateUtils")
    
    def get_current_date(self) -> date:
        """获取当前日期
        
        Returns:
            当前日期对象
        """
        return date.today()
    
    def get_current_datetime(self) -> datetime:
        """获取当前日期时间
        
        Returns:
            当前日期时间对象
        """
        return datetime.now()
    
    def get_current_timestamp(self) -> float:
        """获取当前时间戳
        
        Returns:
            当前Unix时间戳（秒）
        """
        return time.time()
    
    def parse_date(self, date_str: str, format_str: str = "%Y-%m-%d") -> date:
        """从字符串解析日期
        
        Args:
            date_str: 日期字符串
            format_str: 日期格式字符串
            
        Returns:
            解析后的日期对象
            
        Raises:
            DateProcessingException: 日期解析失败
        """
        try:
            return datetime.strptime(date_str, format_str).date()
        except ValueError as e:
            raise DateProcessingException(f"无法解析日期: {str(e)}") from e
    
    def parse_datetime(self, datetime_str: str, format_str: str = "%Y-%m-%d %H:%M:%S") -> datetime:
        """从字符串解析日期时间
        
        Args:
            datetime_str: 日期时间字符串
            format_str: 日期时间格式字符串
            
        Returns:
            解析后的日期时间对象
            
        Raises:
            DateProcessingException: 日期时间解析失败
        """
        try:
            return datetime.strptime(datetime_str, format_str)
        except ValueError as e:
            raise DateProcessingException(f"无法解析日期时间: {str(e)}") from e
    
    def format_date(self, date_obj: Union[date, datetime], format_str: str = "%Y-%m-%d") -> str:
        """格式化日期为字符串
        
        Args:
            date_obj: 日期或日期时间对象
            format_str: 日期格式字符串
            
        Returns:
            格式化后的日期字符串
        """
        return date_obj.strftime(format_str)
    
    def format_datetime(self, datetime_obj: datetime, format_str: str = "%Y-%m-%d %H:%M:%S") -> str:
        """格式化日期时间为字符串
        
        Args:
            datetime_obj: 日期时间对象
            format_str: 日期时间格式字符串
            
        Returns:
            格式化后的日期时间字符串
        """
        return datetime_obj.strftime(format_str)
    
    def timestamp_to_datetime(self, timestamp: float) -> datetime:
        """将时间戳转换为日期时间对象
        
        Args:
            timestamp: Unix时间戳
            
        Returns:
            日期时间对象
        """
        return datetime.fromtimestamp(timestamp)
    
    def datetime_to_timestamp(self, datetime_obj: datetime) -> float:
        """将日期时间对象转换为时间戳
        
        Args:
            datetime_obj: 日期时间对象
            
        Returns:
            Unix时间戳
        """
        return datetime_obj.timestamp()
    
    def add_days(self, date_obj: Union[date, datetime], days: int) -> Union[date, datetime]:
        """添加指定天数
        
        Args:
            date_obj: 日期或日期时间对象
            days: 要添加的天数
            
        Returns:
            新的日期或日期时间对象
        """
        if isinstance(date_obj, datetime):
            return date_obj + timedelta(days=days)
        else:
            return date_obj + timedelta(days=days)
    
    def add_hours(self, datetime_obj: datetime, hours: float) -> datetime:
        """添加指定小时数
        
        Args:
            datetime_obj: 日期时间对象
            hours: 要添加的小时数
            
        Returns:
            新的日期时间对象
        """
        return datetime_obj + timedelta(hours=hours)
    
    def add_minutes(self, datetime_obj: datetime, minutes: float) -> datetime:
        """添加指定分钟数
        
        Args:
            datetime_obj: 日期时间对象
            minutes: 要添加的分钟数
            
        Returns:
            新的日期时间对象
        """
        return datetime_obj + timedelta(minutes=minutes)
    
    def add_seconds(self, datetime_obj: datetime, seconds: float) -> datetime:
        """添加指定秒数
        
        Args:
            datetime_obj: 日期时间对象
            seconds: 要添加的秒数
            
        Returns:
            新的日期时间对象
        """
        return datetime_obj + timedelta(seconds=seconds)
    
    def get_difference_in_days(self, start_date: Union[date, datetime], end_date: Union[date, datetime]) -> float:
        """计算两个日期之间的天数差
        
        Args:
            start_date: 开始日期
            end_date: 结束日期
            
        Returns:
            天数差
        """
        if isinstance(start_date, datetime):
            start_date = start_date.date()
        if isinstance(end_date, datetime):
            end_date = end_date.date()
        
        return (end_date - start_date).days
    
    def get_difference_in_hours(self, start_datetime: datetime, end_datetime: datetime) -> float:
        """计算两个日期时间之间的小时差
        
        Args:
            start_datetime: 开始日期时间
            end_datetime: 结束日期时间
            
        Returns:
            小时差
        """
        return (end_datetime - start_datetime).total_seconds() / 3600
    
    def get_difference_in_minutes(self, start_datetime: datetime, end_datetime: datetime) -> float:
        """计算两个日期时间之间的分钟差
        
        Args:
            start_datetime: 开始日期时间
            end_datetime: 结束日期时间
            
        Returns:
            分钟差
        """
        return (end_datetime - start_datetime).total_seconds() / 60
    
    def get_difference_in_seconds(self, start_datetime: datetime, end_datetime: datetime) -> float:
        """计算两个日期时间之间的秒数差
        
        Args:
            start_datetime: 开始日期时间
            end_datetime: 结束日期时间
            
        Returns:
            秒数差
        """
        return (end_datetime - start_datetime).total_seconds()
    
    def is_leap_year(self, year: int) -> bool:
        """判断是否为闰年
        
        Args:
            year: 年份
            
        Returns:
            是否为闰年
        """
        return calendar.isleap(year)
    
    def get_days_in_month(self, year: int, month: int) -> int:
        """获取指定月份的天数
        
        Args:
            year: 年份
            month: 月份（1-12）
            
        Returns:
            天数
        """
        return calendar.monthrange(year, month)[1]
    
    def get_month_name(self, month: int, full_name: bool = True) -> str:
        """获取月份名称
        
        Args:
            month: 月份（1-12）
            full_name: 是否返回全名
            
        Returns:
            月份名称
        """
        if full_name:
            return calendar.month_name[month]
        else:
            return calendar.month_abbr[month]
    
    def get_weekday_name(self, date_obj: Union[date, datetime], full_name: bool = True) -> str:
        """获取星期几名称
        
        Args:
            date_obj: 日期或日期时间对象
            full_name: 是否返回全名
            
        Returns:
            星期几名称
        """
        weekday = date_obj.weekday()  # 0=Monday, 6=Sunday
        if full_name:
            return calendar.day_name[weekday]
        else:
            return calendar.day_abbr[weekday]
    
    def get_week_range(self, date_obj: Union[date, datetime], week_start: int = 0) -> Tuple[date, date]:
        """获取指定日期所在周的开始和结束日期
        
        Args:
            date_obj: 日期或日期时间对象
            week_start: 一周的开始（0=Monday, 6=Sunday）
            
        Returns:
            (开始日期, 结束日期) 元组
        """
        if isinstance(date_obj, datetime):
            date_obj = date_obj.date()
        
        days_since_week_start = (date_obj.weekday() - week_start) % 7
        week_start_date = date_obj - timedelta(days=days_since_week_start)
        week_end_date = week_start_date + timedelta(days=6)
        
        return (week_start_date, week_end_date)
    
    def get_month_range(self, year: int, month: int) -> Tuple[date, date]:
        """获取指定月份的开始和结束日期
        
        Args:
            year: 年份
            month: 月份
            
        Returns:
            (开始日期, 结束日期) 元组
        """
        start_date = date(year, month, 1)
        end_date = date(year, month, self.get_days_in_month(year, month))
        return (start_date, end_date)
    
    def get_quarter(self, date_obj: Union[date, datetime]) -> int:
        """获取指定日期所在的季度
        
        Args:
            date_obj: 日期或日期时间对象
            
        Returns:
            季度（1-4）
        """
        return (date_obj.month - 1) // 3 + 1
    
    def get_quarter_range(self, year: int, quarter: int) -> Tuple[date, date]:
        """获取指定季度的开始和结束日期
        
        Args:
            year: 年份
            quarter: 季度（1-4）
            
        Returns:
            (开始日期, 结束日期) 元组
            
        Raises:
            DateProcessingException: 无效的季度
        """
        if quarter < 1 or quarter > 4:
            raise DateProcessingException(f"无效的季度: {quarter}")
        
        start_month = (quarter - 1) * 3 + 1
        end_month = quarter * 3
        
        start_date = date(year, start_month, 1)
        end_date = date(year, end_month, self.get_days_in_month(year, end_month))
        
        return (start_date, end_date)
    
    def is_today(self, date_obj: Union[date, datetime]) -> bool:
        """判断日期是否为今天
        
        Args:
            date_obj: 日期或日期时间对象
            
        Returns:
            是否为今天
        """
        if isinstance(date_obj, datetime):
            date_obj = date_obj.date()
        return date_obj == self.get_current_date()
    
    def is_past(self, date_obj: Union[date, datetime]) -> bool:
        """判断日期是否已过期
        
        Args:
            date_obj: 日期或日期时间对象
            
        Returns:
            是否已过期
        """
        if isinstance(date_obj, date) and not isinstance(date_obj, datetime):
            return date_obj < self.get_current_date()
        else:
            return date_obj < self.get_current_datetime()
    
    def is_future(self, date_obj: Union[date, datetime]) -> bool:
        """判断日期是否在未来
        
        Args:
            date_obj: 日期或日期时间对象
            
        Returns:
            是否在未来
        """
        if isinstance(date_obj, date) and not isinstance(date_obj, datetime):
            return date_obj > self.get_current_date()
        else:
            return date_obj > self.get_current_datetime()
    
    def get_business_days(self, start_date: Union[date, datetime], end_date: Union[date, datetime], 
                          holidays: List[date] = None) -> int:
        """计算两个日期之间的工作日数量
        
        Args:
            start_date: 开始日期
            end_date: 结束日期
            holidays: 法定假日列表
            
        Returns:
            工作日数量
        """
        if holidays is None:
            holidays = []
        
        if isinstance(start_date, datetime):
            start_date = start_date.date()
        if isinstance(end_date, datetime):
            end_date = end_date.date()
        
        # 确保start_date不大于end_date
        if start_date > end_date:
            start_date, end_date = end_date, start_date
        
        business_days = 0
        current_date = start_date
        
        while current_date <= end_date:
            # 检查是否为工作日（非周末且非假日）
            if current_date.weekday() < 5 and current_date not in holidays:
                business_days += 1
            current_date += timedelta(days=1)
        
        return business_days
    
    def add_business_days(self, date_obj: Union[date, datetime], days: int, 
                          holidays: List[date] = None) -> Union[date, datetime]:
        """添加指定工作日数量
        
        Args:
            date_obj: 日期或日期时间对象
            days: 要添加的工作日数量
            holidays: 法定假日列表
            
        Returns:
            新的日期或日期时间对象
        """
        if holidays is None:
            holidays = []
        
        is_datetime = isinstance(date_obj, datetime)
        if is_datetime:
            date_part = date_obj.date()
            time_part = date_obj.time()
        else:
            date_part = date_obj
        
        direction = 1 if days > 0 else -1
        remaining_days = abs(days)
        current_date = date_part
        
        while remaining_days > 0:
            current_date += timedelta(days=direction)
            # 检查是否为工作日（非周末且非假日）
            if current_date.weekday() < 5 and current_date not in holidays:
                remaining_days -= 1
        
        if is_datetime:
            return datetime.combine(current_date, time_part)
        else:
            return current_date
    
    def get_next_business_day(self, date_obj: Union[date, datetime], holidays: List[date] = None) -> Union[date, datetime]:
        """获取下一个工作日
        
        Args:
            date_obj: 日期或日期时间对象
            holidays: 法定假日列表
            
        Returns:
            下一个工作日
        """
        return self.add_business_days(date_obj, 1, holidays)
    
    def get_previous_business_day(self, date_obj: Union[date, datetime], holidays: List[date] = None) -> Union[date, datetime]:
        """获取上一个工作日
        
        Args:
            date_obj: 日期或日期时间对象
            holidays: 法定假日列表
            
        Returns:
            上一个工作日
        """
        return self.add_business_days(date_obj, -1, holidays)
    
    def format_relative_time(self, datetime_obj: datetime) -> str:
        """格式化相对时间（例如：5分钟前，3小时前）
        
        Args:
            datetime_obj: 日期时间对象
            
        Returns:
            相对时间描述
        """
        now = self.get_current_datetime()
        diff_seconds = self.get_difference_in_seconds(datetime_obj, now)
        
        if diff_seconds < 0:
            # 未来时间
            diff_seconds = abs(diff_seconds)
            prefix = "在"
            suffix = "后"
        else:
            # 过去时间
            prefix = ""
            suffix = "前"
        
        # 计算各种时间单位
        minutes, seconds = divmod(int(diff_seconds), 60)
        hours, minutes = divmod(minutes, 60)
        days, hours = divmod(hours, 24)
        weeks, days = divmod(days, 7)
        months, days = divmod(days, 30)  # 近似值
        years, months = divmod(months, 12)
        
        # 构建相对时间描述
        if years > 0:
            return f"{prefix}{years}年{suffix}"
        elif months > 0:
            return f"{prefix}{months}个月{suffix}"
        elif weeks > 0:
            return f"{prefix}{weeks}周{suffix}"
        elif days > 0:
            return f"{prefix}{days}天{suffix}"
        elif hours > 0:
            return f"{prefix}{hours}小时{suffix}"
        elif minutes > 0:
            return f"{prefix}{minutes}分钟{suffix}"
        else:
            return f"{prefix}{seconds}秒{suffix}"

if __name__ == "__main__":
    # 示例用法
    date_utils = DateUtils()
    
    # 测试日期格式化
    print("=== 测试日期格式化 ===")
    current_date = date_utils.get_current_date()
    current_datetime = date_utils.get_current_datetime()
    print(f"当前日期: {date_utils.format_date(current_date)}")
    print(f"当前日期时间: {date_utils.format_datetime(current_datetime)}")
    print(f"当前日期（自定义格式）: {date_utils.format_date(current_date, '%d/%m/%Y')}")
    print(f"当前日期时间（自定义格式）: {date_utils.format_datetime(current_datetime, '%d-%m-%Y %H:%M:%S')}")
    
    # 测试日期计算
    print("\n=== 测试日期计算 ===")
    future_date = date_utils.add_days(current_date, 7)
    past_date = date_utils.add_days(current_date, -7)
    print(f"7天后的日期: {date_utils.format_date(future_date)}")
    print(f"7天前的日期: {date_utils.format_date(past_date)}")
    print(f"天数差: {date_utils.get_difference_in_days(past_date, future_date)}")
    print(f"是否为今天: {date_utils.is_today(current_date)}")
    print(f"是否已过期: {date_utils.is_past(past_date)}")
    print(f"是否在未来: {date_utils.is_future(future_date)}")
    
    # 测试工作日计算
    print("\n=== 测试工作日计算 ===")
    # 假设周末为非工作日
    business_days = date_utils.get_business_days(past_date, future_date)
    print(f"7天内的工作日数量: {business_days}")
    next_business_day = date_utils.get_next_business_day(current_date)
    print(f"下一个工作日: {date_utils.format_date(next_business_day)}")
    
    # 测试相对时间格式化
    print("\n=== 测试相对时间格式化 ===")
    five_minutes_ago = date_utils.add_minutes(current_datetime, -5)
    one_hour_later = date_utils.add_hours(current_datetime, 1)
    print(f"5分钟前: {date_utils.format_relative_time(five_minutes_ago)}")
    print(f"1小时后: {date_utils.format_relative_time(one_hour_later)}")
    
    # 测试月份和季度信息
    print("\n=== 测试月份和季度信息 ===")
    print(f"当前月份天数: {date_utils.get_days_in_month(current_date.year, current_date.month)}")
    print(f"月份名称: {date_utils.get_month_name(current_date.month)}")
    print(f"星期几: {date_utils.get_weekday_name(current_date)}")
    print(f"当前季度: {date_utils.get_quarter(current_date)}")
    quarter_start, quarter_end = date_utils.get_quarter_range(current_date.year, date_utils.get_quarter(current_date))
    print(f"当前季度范围: {date_utils.format_date(quarter_start)} - {date_utils.format_date(quarter_end)}")